/**
 * @file lv_port_disp.c
 *
 */

#include "lv_port.h"

#include <stdbool.h>
#include <stdio.h>

#ifdef CONFIG_SDL2_LCD
#include "lcd/sdl2_lcd.h"
/* 设置屏幕分辨率 */
#define MY_DISP_HOR_RES CONFIG_LV_DISP_HOR_RES
#define MY_DISP_VER_RES CONFIG_LV_DISP_VER_RES

static sdl2_lcd lcd;

static void sdl2_disp_flush(lv_display_t* disp, const lv_area_t* area, uint8_t* px_map);
#endif

#ifdef CONFIG_LV_USE_LINUX_FBDEV
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/ioctl.h>

#define FB_NAME "/dev/fb0"

static void get_fb_screen_information(struct screen * screen);
#endif

struct screen g_screen;

void lv_port_disp_init(void)
{
#ifdef CONFIG_SDL2_LCD
	/*-------------------------
	 * Initialize your display
	 * -----------------------*/
	lcd.cfg.fix.width = MY_DISP_HOR_RES;
	lcd.cfg.fix.height = MY_DISP_VER_RES;
	sdl2_lcd_init(&lcd);
	g_screen.x = MY_DISP_HOR_RES;
	g_screen.y = MY_DISP_VER_RES;

	/*------------------------------------
	 * Create a display and set a flush_cb
	 * -----------------------------------*/
	lv_display_t* disp = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
	lv_display_set_flush_cb(disp, sdl2_disp_flush);

	/* Example 3
	 * Two buffers screen sized buffer for double buffering.
	 * Both LV_DISPLAY_RENDER_MODE_DIRECT and LV_DISPLAY_RENDER_MODE_FULL works, see their comments*/
	static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];
	static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES];
	lv_display_set_buffers(disp, buf_3_1, buf_3_2, sizeof(buf_3_1), LV_DISPLAY_RENDER_MODE_PARTIAL);
#endif
#ifdef CONFIG_LV_USE_LINUX_FBDEV
	get_fb_screen_information(&g_screen);
	printf("Screen resolution: %d x %d\n", g_screen.x, g_screen.y);

	lv_display_t* disp = lv_linux_fbdev_create(g_screen.x, g_screen.y);
	lv_linux_fbdev_set_file(disp, FB_NAME);
#endif
}

#ifdef CONFIG_LV_USE_LINUX_FBDEV
static void get_fb_screen_information(struct screen * screen)
{
	int fb;
	struct fb_var_screeninfo fb_var;

	// 打开 /dev/fb0
	fb = open(FB_NAME, O_RDONLY);
	if (fb < 0) {
		printf("Error: cannot open framebuffer");
		return;
	}

	// 获取帧缓冲设备的属性
	if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var)) {
		printf("Error: cannot get framebuffer info");
		close(fb);
		return;
	}

	screen->x = (unsigned int)fb_var.xres;
	screen->y = (unsigned int)fb_var.yres;
	// 关闭 /dev/fb0
	close(fb);
}
#endif

struct screen get_screen(void)
{
	return g_screen;
}

#ifdef CONFIG_SDL2_LCD
volatile bool disp_flush_enabled = true;

/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL
 */
void disp_enable_update(void)
{
	disp_flush_enabled = true;
}

/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL
 */
void disp_disable_update(void)
{
	disp_flush_enabled = false;
}

/*Flush the content of the internal buffer the specific area on the display.
 *`px_map` contains the rendered image as raw pixel map and it should be copied to `area` on the display.
 *You can use DMA or any hardware acceleration to do this operation in the background but
 *'lv_display_flush_ready()' has to be called when it's finished.*/
static void sdl2_disp_flush(lv_display_t* disp_drv, const lv_area_t* area, uint8_t* px_map)
{
	if (disp_flush_enabled) {
		sdl2_lcd_fill(&lcd, area->x1, area->y1, area->x2, area->y2, (uint16_t*)px_map);
	}

	/*IMPORTANT!!!
	 *Inform the graphics library that you are ready with the flushing*/
	lv_display_flush_ready(disp_drv);
}
#endif
